package ship;

import graphics.Drawing;

import graphics.MaxGraphics;

import java.awt.Color;
import java.awt.Polygon;
import java.awt.Shape;
import java.awt.geom.Ellipse2D;
import java.util.Vector;

import javax.swing.ComboBoxModel;

import animation.ExplodingShip;

import objects.Asteroid;

import main.Constants;
import main.Game;

import secretweapons.Pulse;
import structure.*;
import util.LinAlg;
import util.Point;
import util.Timer;
import weapons.Shot;

public class Ship extends SObject {
	private int player;
	
	/** Positioning variables **/
	private double angle = 0;
	private double angleVel = 0;
	
	/** Damage and power **/
	
	private int[] damage = new int[ShipConstants.model.length];
	private int[] power = new int[ShipConstants.systems.length];
	private int spare = 2;
	
	/** Shield info **/
	
	private Timer shieldAnimTimer = new Timer(40, 45, .1, true);
	private double shields = 1;
	private double tempShields = 0;
	private Timer shieldTimeoutTimer = new Timer(30, false);
	
	/** Misc **/
	private Timer jumpCooldownTimer = new Timer(1000, false);
	private Timer shotTimer = new Timer(10, false);
	
	private Vector<String> weapons = new Vector<String>();
	private Vector<Timer> weaponTimers = new Vector<Timer>();
	private Vector<String> specials = new Vector<String>();
	private Vector<Timer> specialTimer = new Vector<Timer>();
	
	private String rightTube, leftTube, centerTube;

	public Ship(Point p, int player) {
		super(p);
		shieldAnimTimer.start();
		jumpCooldownTimer.start();
		shotTimer.start();
		this.player = player;
		for(int i = 0; i < damage.length; i++) {
			damage[i] = 0;
		}
		for(int i = 0; i < power.length; i++) {
			power[i] = 2;
		}
		weapons.add("Static Mine");
		specials.add("Gravity Pulse");
		// TODO Auto-generated constructor stub
	}
	
	public void update() {
		angle += angleVel;
		dampen();
		
		shieldTimeoutTimer.count();
		if(shieldTimeoutTimer.ready()) {
			shields = tempShields;
			shieldTimeoutTimer.stop();
		}
		if(shieldTimeoutTimer.isStopped()) {
			shields += .0001 * (power[ShipConstants.SHIELDS] - damage[ShipConstants.MID_BODY]);
			shields = Math.max(shields, 0);
			shields = Math.min(1, shields);
		}
		
		shotTimer.count();
		jumpCooldownTimer.count();
		super.update();
	}

	/** Movement **/
	
	public void rotate(int i) {
		angleVel += i * (2 * ShipConstants.ANGLE_ACC - ShipConstants.ANGLE_ACC / ShipConstants.MAX_ANGLE_VEL * Math.abs(angleVel));
		angleVel = Math.max(-ShipConstants.MAX_ANGLE_VEL, Math.min(angleVel,ShipConstants.MAX_ANGLE_VEL));
	}
	
	public void acc(int i) {
		double x = -i *Math.sin(angle) * (Math.max(ShipConstants.ACC + ((getEngineStrength() - 1)), 0));
		double y = i* Math.cos(angle) * (Math.max(ShipConstants.ACC + ((getEngineStrength() - 1)) , 0));
		if(Math.sqrt( Math.pow((x + super.vel.getX()) , 2) + Math.pow((y + super.vel.getY()) , 2) ) <= ShipConstants.MAX_VEL){
			super.acc(new Point(x, y));
		}
	}
	
	public void jump() {
		if(jumpCooldownTimer.ready() && (power[ShipConstants.ENGINES] - damage[ShipConstants.REAR_BODY]) >= ShipConstants.JUMP_MIN) {
			this.cords = new Point(cords.getX() + (100 * vel.getX()), cords.getY() + (100* vel.getY()));
			jumpCooldownTimer.start();
		}
	}
	
	public void dampen() {
		if(angleVel > 0){
			angleVel = Math.max(0 , angleVel - (ShipConstants.ANGLE_DAMP / (damage[ShipConstants.LEFT_COND]+ 1) ));
		}
		else{
			angleVel = Math.min(0 , angleVel + ShipConstants.ANGLE_DAMP / (damage[ShipConstants.RIGHT_COND]+ 1) );
		}

		double velMag = super.vel.getMag();
		double velAngle = super.vel.getAngle();
		velMag = Math.max(velMag-ShipConstants.DAMP, 0);
		super.setVel(new Point(velMag * Math.cos(velAngle) , velMag * Math.sin(velAngle)));

	}
	
	/** Weapons **/
	
	public void fire(boolean left, boolean right) {
		
		if(shotTimer.ready()) {
			if(power[ShipConstants.WEAPONS] == 0) {
				return;
			}
			if(shieldTimeoutTimer.isStopped()) {
				tempShields = shields;
				shields = 0;
			}
			shieldTimeoutTimer.start();
			if(left && power[ShipConstants.WEAPONS] - damage[ShipConstants.LEFT_GUN] > 0)
				fireLeft();
			if(right && power[ShipConstants.WEAPONS] - damage[ShipConstants.RIGHT_GUN] > 0)
				fireRight();
			shotTimer.reset();
		}
	}


	public void fireLeft() {
		Point p2 = new Point(-25, 5+10+20);
		p2 = p2.rotate(angle);
		p2 = p2.translate(getX(), getY());
		Shot newShot = new Shot(p2, 
				new Point((-Math.sin(angle -ShipConstants.CONVERGE) * (getShotVel(true)) + super.vel.getX()), 
						(Math.cos(angle - ShipConstants.CONVERGE) * getShotVel(true)) + super.vel.getY()), 
						ShipConstants.SHOT_DAMAGE* power[ShipConstants.WEAPONS]);
		Game.game.registerObject(newShot, (Constants.MOVE | Constants.DRAW | Constants.COLLIDE));
		//Game.game.send(newShot);
	}

	public double getShotVel(boolean left) {
		if(left) 
			return ShipConstants.SHOT_VEL + ((power[ShipConstants.WEAPONS] - damage[ShipConstants.LEFT_GUN]) * 5);
		else
			return ShipConstants.SHOT_VEL + ((power[ShipConstants.WEAPONS] - damage[ShipConstants.RIGHT_GUN]) * 5);
	}

	public void fireRight() {
		Point p1 = new Point(25, 5+10+20);
		p1 = p1.rotate(angle);
		p1 = p1.translate(getX(), getY());
		Shot newShot = new Shot(p1, 
				new Point((-Math.sin(angle + ShipConstants.CONVERGE) * getShotVel(false)) + super.vel.getX(), 
						(Math.cos(angle+ ShipConstants.CONVERGE) * getShotVel(false)) + super.vel.getY()),
						ShipConstants.SHOT_DAMAGE* power[ShipConstants.WEAPONS]);
		Game.game.registerObject(newShot, 
						(Constants.MOVE | Constants.DRAW | Constants.COLLIDE));
		//Game.game.send(newShot);
	}
	
	public void manageShields() {
		shieldTimeoutTimer.count();
		if(shieldTimeoutTimer.ready()) {
			shields = tempShields;
			shieldTimeoutTimer.stop();
		}
		if(shieldTimeoutTimer.isStopped()) {
			shields += .0001 * (power[ShipConstants.SHIELDS] - damage[ShipConstants.MID_BODY]);
			shields = Math.max(shields, 0);
			shields = Math.min(1, shields);
		}
	}
	
	@Override
	public void draw(MaxGraphics g) {
		shieldAnimTimer.count();
		Drawing.drawShip(g, cords, angle, shields, shieldAnimTimer.getValue());
	}
	

	@Override
	public void collide(SObject o) {
		if(o instanceof Asteroid || o instanceof Shot) {
			if(shields > .1) {
				shields = Math.max(0, shields - ((Damaging)o).getDamage());
			}
			else {
				Point p = o.getCords();

				for(int i = 0; i < ShipConstants.model.length; i++) {
					Polygon newPoly = LinAlg.rotate(ShipConstants.model[i], angle);
					newPoly.translate((int) this.getX(), (int) this.getY());
					Shape[] n = {newPoly};

					if(LinAlg.checkCollide(o.getShape(), n)) {
						damage(i);
						newPoly.translate(-(int) this.getX(), -(int) this.getY());
						return;
					}
				}
			}
		}
	}
	
	public void damage(int index) {
		if(index == ShipConstants.FRONT_BODY || index == ShipConstants.MID_BODY || index == ShipConstants.REAR_BODY) {
			if(damage[index] == 2 && ! flagged) {
				flag();
				Game.game.registerObject(new ExplodingShip(cords, angle), Constants.DRAW);
			}
		}
		damage[index] = Math.min(2, damage[index]+1);
		if(index == ShipConstants.MID_BODY) {
			spare--;
		}
	}

	
	/** Getters and setters **/
	
	@Override
	public Shape[] getShape() {
		if(shields > .1) {
			Shape[] s = { new Ellipse2D.Double(getX() - (getRadius()/2), getY() - (getRadius()/2), getRadius(), getRadius())};
			return s;
		}
		else {
			Polygon[] shapes = new Polygon[ShipConstants.model.length];
			for(int i = 0; i < shapes.length; i++) {
				shapes[i] = LinAlg.rotate(ShipConstants.model[i], angle);
				shapes[i].translate((int) getX(), (int) getY());
			}
			return shapes;
		}
	}

	@Override
	public double getRadius() {
		return ShipConstants.RADIUS;
	}

	public int getPlayer() {
		return player;
	}
	
	public int getSensorRadius() {
		return ShipConstants.SENSOR_RADIUS * power[ShipConstants.SENSORS] / (damage[ShipConstants.FRONT_BODY] + 1);
	}

	@Override
	public String toString() {
		return "Ship " + getPlayer() + " " +getX() + " " + getY() + " " + angle + " " + shields + " " + shieldAnimTimer.getValue();
	}
	
	public void adjust(Point cords, double angle) {
		this.cords = new Point(cords);
		this.angle = angle;
	}
	
	public double getShields() {
		return shields;
	}
	
	public int getDamage(int i) {
		return damage[i];
	}

	public double getTimeTillJump() {
		if(getEngineStrength() < 4) {
			return -Double.MAX_VALUE;
		}
		else {
			return jumpCooldownTimer.getValue();
		}
	}
	
	public void setPower(int[] power) {
		this.power = power;
	}
	
	public int getEngineStrength() {
		return power[ShipConstants.ENGINES] - damage[ShipConstants.REAR_BODY];
	}

	public void increasePower(int system) {
		if(spare > 0 && power[system] < 4) {
			spare--;
			power[system]++;
		}
	}
	
	public void decreasePower(int system) {
		if(power[system] > 0) {
			power[system]--;
			spare++;
		}
	}

	public int sparePower() {
		// TODO Auto-generated method stub
		return spare;
	}
	
	public int[] getPower() {
		return power;
	}



	public void changeLeftTube(int selectedIndex) {
		leftTube = weapons.get(selectedIndex);
		
	}

	public int getTimeLeft() {
		// TODO Auto-generated method stub
		return 0;
	}

	public void changeRightTube(int selectedIndex) {
		rightTube = weapons.get(selectedIndex);
		
	}

	public void changeCenterTube(int selectedIndex) {
		centerTube = weapons.get(selectedIndex);
		
	}

	public int getTimeRight() {
		// TODO Auto-generated method stub
		return 0;
	}

	public int getTimeCenter() {
		// TODO Auto-generated method stub
		return 0;
	}
	
	public void addWeapon(String s) {
		weapons.add(s);
	}
	
	public String[] getWeapons() {
		String[] weapons = new String[this.weapons.size()];
		for(int i = 0 ; i < this.weapons.size(); i++) {
			weapons[i] = this.weapons.get(i);
		}
		return weapons;
	}
	
	public int getLeft() {
		return weapons.indexOf(leftTube);
	}
	public int getRight() {
		return weapons.indexOf(leftTube);
	}

	public void fireCenterTube() {
		Game.game.registerAll(new Pulse(cords, shields));
		shields = 0;
		
	}

	public void fireRightTube() {
		// TODO Auto-generated method stub
		
	}

	public String[] getSpecials() {
		// TODO Auto-generated method stub
		String[] specials = new String[this.specials.size()];
		for(int i = 0 ; i < this.specials.size(); i++) {
			specials[i] = this.specials.get(i);
		}
		return specials;
	}

}
